// 练习2
// 实现一个依赖类和响应区函数，
// 使得当任意进入响应式空间的函数都能作为依赖被收集
// 被收集的依赖相当于是被订阅，当依赖类的状态更新时
// 依赖类应该可以提醒并激活所有的订阅者更新所有的状态

// 标记正在运行的函数
let isRunning: DepFunction | null;
// 依赖函数类型
type DepFunction = <T = number>(dep: Dependency, ...args: Array<T>) => any;

// 依赖类定义
class Dependency {
  subscribers: Set<DepFunction>; // 依赖队列（订阅者）,需要去重
  constructor() {
    this.subscribers = new Set<DepFunction>();
  }
  // 表明某段runtime代码被依赖
  depend() {
    if (isRunning) {
      this.subscribers.add(isRunning); // 订阅当前的依赖函数
    }
  }
  // 表明某段runtime代码的依赖被触发，代码执行
  notify() {
    this.subscribers.forEach((func) => {
      func(dep);
    }); // 执行当前的依赖
  }
}

const dep = new Dependency(); // 依赖对象

// 响应式空间
function reactivity<T = number>(func: DepFunction, ...args: T[]) {
  console.log("reactivity是响应式空间，目前处于反应区。");
  // 追踪依赖的函数
  function traceFunc() {
    isRunning = traceFunc; // 标记正在进行的函数
    dep.depend(); // 收集当前依赖
    func<T>(dep, ...args);
    isRunning = null;
  }
  traceFunc(); // 重新执行函数
}

// 测试用例
let quantity = 10;
let price = 10;
let total = 0;
let calPrice = () => total = quantity*price;

reactivity(calPrice);
console.log(total);// 100
price = 20;
console.log(total);// 100
dep.notify();// 更新当前依赖状态
console.log(total);// 200
